<?php
namespace app\web\command;

use think\console\Command;
use think\console\Input;
use think\console\Output;
use app\common\component\Redis;
use app\web\mongo\BackendUser;

/**
 * cli脚本启动swoole服务端服务
 * Class Swoole
 * @package app\web\command
 */
class Swoole extends Command
{
    // mongodb会连接超时,需要记录连接时间，固定时间重建连接
    private $mongoLinkTime = 0;
    /** @var null swoole对象 */
    private $ws = null;

    protected function configure()
    {
        $this->setName('start-up swoole')->setDescription('start up the swoole server');
    }

    /** 验证是否正确 */
    private function isCorrectData($data)
    {
        if (!is_array($data)) {
            return false;
        }

        if (!isset($data['type'], $data['content'], $data['user_id'])) {
            return false;
        }

        if (!in_array($data['type'], [1, 2, 3, 4, 5])) {
            return false;
        }

        return true;
    }

    /**
     * 业务逻辑
     * 服务端接受: type: 1:心跳包 2:新用户连接  3: 发送消息 4: 寻找朋友 5: 断开连接朋友
     * 客户端接受: type: 1:心跳包 2:系统消息    3: 朋友消息
     * @param $data
     */
    protected function businessLogic($data)
    {
        if ($data['type'] == 1) {// 心跳响应
            $sendData = json_encode(['type' => 1]);
            $this->ws->push($data['swooleId'], $sendData);
        } else {
            switch ($data['type']) {
                case 2:// 连接用户
                    $this->joinSwoolePool($data['swooleId'], $data['user_id']);
                    break;
                case 3:// 发送消息
                    // 获取好友
                    $friendId = Redis::getInstance()->hget(Redis::USER_FRIEND_RELATIONSHIP, $data['swooleId']);
                    if ($friendId) {
                        $this->ws->push($friendId, json_encode(['type' => 3, 'content' => $data['content']]));
                    } else {
                        $this->ws->push($data['swooleId'], json_encode(['type' => 2, 'content' => '您没有好友']));
                    }
                    break;
                case 4:// 连接朋友
                    $this->matchFriend($data['swooleId']);
                    break;
                case 5:// 断开连接朋友
                    $this->disconnectFriend($data['swooleId']);
                    break;
            }
        }
    }

    // 重新启动swoole的时候, 进行相关swoole的初始化
    public function initSwooleRedis()
    {   // 清除redis缓存
        Redis::getInstance()->del(Redis::SWOOLE_USER_POOL);
        Redis::getInstance()->del(Redis::SWOOLE_AVAILABLE_USER_POOL);
        Redis::getInstance()->del(Redis::SWOOLE_USER_RELATION);
        Redis::getInstance()->del(Redis::USER_FRIEND_RELATIONSHIP);
    }

    /** 加入连接池 */
    public function joinSwoolePool($swooleId, $userId)
    {
        // 加入swoole用户连接池
        Redis::getInstance()->sadd(Redis::SWOOLE_USER_POOL, $swooleId);
        // 加入swoole可连接的用户池
        Redis::getInstance()->sadd(Redis::SWOOLE_AVAILABLE_USER_POOL, $swooleId);
        // 构建user_id 与 swoole_id的hash关系
        Redis::getInstance()->hset(Redis::SWOOLE_USER_RELATION, $swooleId, $userId);
    }

    /** 移除连接池 */
    public function removeSwoolePool($swooleId)
    {
        // 移除swoole用户连接池
        Redis::getInstance()->srem(Redis::SWOOLE_USER_POOL, $swooleId);
        // 移除可连接的swoole用户连接池
        Redis::getInstance()->srem(Redis::SWOOLE_AVAILABLE_USER_POOL, $swooleId);
        // 删除user_id 与 swoole_id的hash关系
        Redis::getInstance()->hdel(Redis::SWOOLE_USER_RELATION, $swooleId);
        // 判断是否有好友关系
        $friendId = Redis::getInstance()->hget(Redis::USER_FRIEND_RELATIONSHIP, $swooleId);
        if ($friendId) {
            // 解除好友关系
            Redis::getInstance()->hdel(Redis::USER_FRIEND_RELATIONSHIP, $swooleId, $friendId);
            // 好友再次加入可连接的swoole用户连接池
            Redis::getInstance()->sadd(Redis::SWOOLE_AVAILABLE_USER_POOL, $friendId);
            $this->ws->push($friendId, json_encode(['type' => 2, 'content' => '您的好友已经断开连接', 'userId' => false]));
        }
    }

    /** 获取好友 */
    private function getRandomFriend($swooleId)
    {
        $friendIdList = Redis::getInstance()->srandmember(Redis::SWOOLE_AVAILABLE_USER_POOL, 2);
        foreach ($friendIdList as $friendId) {
            if ($friendId != $swooleId) {
                return $friendId;
            }
        }

        return false;
    }

    /** 匹配聊天好友 */
    public function matchFriend($swooleId)
    {
        $friendId = $this->getRandomFriend($swooleId);
        if ($friendId) {// 有则给两端用户发消息
            if ($friendId != $swooleId) {
                // 建立朋友关系
                Redis::getInstance()->srem(Redis::SWOOLE_AVAILABLE_USER_POOL, $swooleId);
                Redis::getInstance()->srem(Redis::SWOOLE_AVAILABLE_USER_POOL, $friendId);
                Redis::getInstance()->hmset(Redis::USER_FRIEND_RELATIONSHIP, ["$friendId" => $swooleId, "$swooleId" => $friendId]);
                $this->ws->push($friendId, json_encode(['type' => 2, 'content' => '匹配到用户了', 'userId' => true]));
                $this->ws->push($swooleId, json_encode(['type' => 2, 'content' => '匹配到用户了', 'userId' => true]));
            } else {
                // 加入可连接的swoole用户连接池
                Redis::getInstance()->sadd(Redis::SWOOLE_AVAILABLE_USER_POOL, $friendId);
                $this->ws->push($swooleId, json_encode(['type' => 2, 'content' => '当前没有用户在线,请稍后尝试']));
            }
        } else {// 没有匹配到用户
            // 加入可连接的swoole用户连接池
            $this->ws->push($swooleId, json_encode(['type' => 2, 'content' => '当前没有用户在线,请稍后尝试']));
        }
    }

    /** 断开连接 */
    public function disconnectFriend($swooleId)
    {
        $friendId = Redis::getInstance()->hget(Redis::USER_FRIEND_RELATIONSHIP, $swooleId);
        if ($friendId) {
            Redis::getInstance()->hdel(Redis::USER_FRIEND_RELATIONSHIP, $swooleId, $friendId);
            Redis::getInstance()->sadd(Redis::SWOOLE_AVAILABLE_USER_POOL, $swooleId, $friendId);
            $this->ws->push($swooleId, json_encode(['type' => 2, 'content' => '您已经断开连接了', 'userId' => false]));
            $this->ws->push($friendId, json_encode(['type' => 2, 'content' => '您的好友已经断开连接', 'userId' => false]));
        } else {
            $this->ws->push($swooleId, json_encode(['type' => 2, 'content' => '您没有好友,无法断开连接', 'userId' => false]));
        }
    }

    /**
     * type: 1:心跳包(50秒一次), 2: 匹配好友 3: 发送消息
     */
    protected function execute(Input $input, Output $output)
    {
        $output->writeln("启动swoole\n");
        $this->ws = new \Swoole\WebSocket\Server('172.17.0.15', 65432);
        $swooleObj = $this;
        // 监听WebSocket连接打开事件
        $this->ws->on('Open', function ($ws, $request) use ($swooleObj) {
            $ws->push($request->fd, json_encode(['type' => 2, 'content' => '欢迎来到聊天室']));
            echo "客户端-{$request->fd} 连接了\n";
        });

        // 监听WebSocket消息事件
        $this->ws->on('Message', function ($ws, $frame) use($swooleObj) {
            $data = json_decode($frame->data, true);
            if ($swooleObj->isCorrectData($data)) {
                $data['swooleId'] = $frame->fd;
                $swooleObj->businessLogic($data);
            } else {
                $ws->push($frame->fd, json_encode(['type' => 2, 'content' => '错误消息格式']));
            }
            echo "客户端-{$frame->fd} 消息是: " . $frame->data . "\n";
        });

        // 监听WebSocket连接关闭事件
        $this->ws->on('Close', function ($ws, $fd) use ($swooleObj) {
            $swooleObj->removeSwoolePool($fd);
            echo "客户端-{$fd} 断开了连接 \n";
        });

        $this->initSwooleRedis();
        $this->ws->start();// 这之后的代码不会执行了？
    }
}